package com.uuah.server.transport.provider.mina.filter;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.commons.lang.StringUtils;
import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.write.WriteRequest;
import org.apache.mina.filter.firewall.Subnet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.api.cache.ICache;
import com.uuah.server.transport.provider.mina.MinaProperties;
import com.uuah.utils.PropertiesUtils;

/**
 *
 * TODO comment ...
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午02:18:12
 * @version 1.0.0
 */
public class WhitelistFilter extends IoFilterAdapter {
	private transient final Logger logger = LoggerFactory.getLogger(getClass());
	private final List<Subnet> whiteList = new CopyOnWriteArrayList<Subnet>();
	private ICache localCache;
	private Properties ipProps;

	public WhitelistFilter() {
		super();
		init();
	}

	public void init() {
		try {
			ipProps = PropertiesUtils.loadProperties("authIp.properties",
					WhitelistFilter.class);
			Set<Object> keys = ipProps.keySet();
			InetAddress address;
			for (Object key : keys) {
				address = InetAddress.getByName(key.toString());
				addAuthAddr(address);
			}
		} catch (IOException e) {
			logger.error(e.getMessage());
		}
	}

	public List<Subnet> getWhiteList() {
		return whiteList;
	}

	public void setWhitelist(InetAddress[] addresses) {
		if (addresses == null) {
			throw new NullPointerException("addresses");
		}
		whiteList.clear();
		for (int i = 0; i < addresses.length; i++) {
			InetAddress addr = addresses[i];
			addAuthAddr(addr);
		}
	}

	public void setSubnetWhiteList(Subnet[] subnets) {
		if (subnets == null) {
			throw new NullPointerException("Subnets must not be null");
		}
		whiteList.clear();
		for (Subnet subnet : subnets) {
			addAuthAddr(subnet);
		}
	}

	public void setWhiteList(Iterable<InetAddress> addresses) {
		if (addresses == null) {
			throw new NullPointerException("addresses");
		}

		whiteList.clear();

		for (InetAddress address : addresses) {
			addAuthAddr(address);
		}
	}

	public void setSubnetWhiteList(Iterable<Subnet> subnets) {
		if (subnets == null) {
			throw new NullPointerException("Subnets must not be null");
		}
		whiteList.clear();
		for (Subnet subnet : subnets) {
			addAuthAddr(subnet);
		}
	}

	public void addAuthAddr(InetAddress address) {
		if (address == null) {
			throw new NullPointerException("Adress to block can not be null");
		}

		addAuthAddr(new Subnet(address, 32));
	}

	public void addAuthAddr(Subnet subnet) {
		if (subnet == null) {
			throw new NullPointerException("Subnet can not be null");
		}

		whiteList.add(subnet);
	}

	public void removeAuthAddr(InetAddress address) {
		if (address == null) {
			throw new NullPointerException("Adress to unblock can not be null");
		}

		removeAuthAddr(new Subnet(address, 32));
	}

	public void removeAuthAddr(Subnet subnet) {
		if (subnet == null) {
			throw new NullPointerException("Subnet can not be null");
		}
		whiteList.remove(subnet);
	}

	@Override
	public void sessionCreated(NextFilter nextFilter, IoSession session) {
		if (isAuthed(session)) {
			nextFilter.sessionCreated(session);
		} else {
			blockSession(session);
		}
	}

	@Override
	public void sessionOpened(NextFilter nextFilter, IoSession session)
			throws Exception {
		if (isAuthed(session)) {
			nextFilter.sessionOpened(session);
		} else {
			blockSession(session);
		}
	}

	@Override
	public void sessionClosed(NextFilter nextFilter, IoSession session)
			throws Exception {
		if (isAuthed(session)) {
			nextFilter.sessionClosed(session);
		} else {
			blockSession(session);
		}
	}

	@Override
	public void sessionIdle(NextFilter nextFilter, IoSession session,
			IdleStatus status) throws Exception {
		nextFilter.sessionIdle(session, status);
	}

	@Override
	public void messageReceived(NextFilter nextFilter, IoSession session,
			Object message) {
		nextFilter.messageReceived(session, message);
	}

	@Override
	public void messageSent(NextFilter nextFilter, IoSession session,
			WriteRequest writeRequest) throws Exception {
		nextFilter.messageSent(session, writeRequest);

	}

	private void blockSession(IoSession session) {
		logger.warn("Remote address not in the whiteList; closing.");
		session.close();
	}

	private boolean isAuthed(IoSession session) {
		SocketAddress remoteAddress = session.getRemoteAddress();
		if (remoteAddress instanceof InetSocketAddress) {
			InetAddress address = ((InetSocketAddress) remoteAddress)
					.getAddress();

			for (Subnet subnet : whiteList) {
				if (subnet.inSubnet(address)) {
					String appRole = ipProps.getProperty(address
							.getHostAddress());
					if (StringUtils.isNotBlank(appRole)
							&& appRole.indexOf("_") != -1) {
						String[] appRoleArray = StringUtils.split(appRole, "_");
						session.setAttributeIfAbsent(
								MinaProperties.MINA_SESSION_APP_PROPERTY,
								appRoleArray[0]);
						session.setAttributeIfAbsent(
								MinaProperties.MINA_SESSION_ROLE_PROPERTY,
								appRoleArray[1]);
					}
					return true;
				}
			}
		}

		return false;
	}
}
